package com.example.demoproject.utils;

import com.example.demoproject.common.ErrorCode;
import com.example.demoproject.model.vo.OrderVo;
import com.example.exception.OrderException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author wangdesheng
 */
public class OrderUtils {

    private static final String FILE_SEPARATOR = File.separator;
    private static final String FILE_DIRECTORY = "src/main/resources/";

    /**
     *
     * @param orderTime
     */
    public static void ensureDirectoryFileExists(Date orderTime) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(orderTime);

        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);

        File yearDir = new File(FILE_DIRECTORY + Integer.toString(year));
        if (!yearDir.exists()) {
            yearDir.mkdirs();
        }

        File monthDir = new File(FILE_DIRECTORY +year + FILE_SEPARATOR + month);
        if (!monthDir.exists()) {
            monthDir.mkdirs();
        }

        File file = new File(FILE_DIRECTORY +year + FILE_SEPARATOR + month + FILE_SEPARATOR + day + ".txt");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取[year]年所有节假日列表
     * @param year
     * @return
     */
    public static List<String> getWeekendList(int year) {
        List<String> weekendList = new ArrayList<>();
        String filename = year + "_weekend.txt";
        String filePath = FILE_DIRECTORY + filename;
        File file = new File(filePath);
        if(!file.exists()) {
            throw new OrderException(ErrorCode.PARAM_ERROR, "节假日文件不存在");
        }
        try (Scanner scanner = new Scanner(new File(filePath))) {
            while (scanner.hasNextLine()) {
                weekendList.add(scanner.nextLine().trim());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return weekendList;
    }

    /**
     * 根据订单时间, 生成订单编号
     * @param orderTime
     * @return
     */
    public static String getOrderNumber(Date orderTime) {
        ensureDirectoryFileExists(orderTime);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(orderTime);

        String path = FILE_DIRECTORY + calendar.get(Calendar.YEAR) + FILE_SEPARATOR +
                (calendar.get(Calendar.MONTH) + 1) + FILE_SEPARATOR +
                calendar.get(Calendar.DAY_OF_MONTH) + ".txt";

        int randomNum;
        List<String> existNumList = new ArrayList<>();

        try (Scanner scanner = new Scanner(new File(path))) {
            while (scanner.hasNextLine()) {
                existNumList.add(scanner.nextLine().trim());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        Random random = new Random();
        randomNum = 0;
        int maxAttempts = 2000;
        for (int i = 0; i < maxAttempts; i++) {
            randomNum = random.nextInt(10000);
            if (!existNumList.contains(Integer.toString(randomNum))) {
                try (FileWriter fileWriter = new FileWriter(path, true)) {
                    fileWriter.write(randomNum + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }

        return String.format("%04d", randomNum);
    }

    /**
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static Date getAWorkDay(Date startDate, Date endDate) {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(startDate);

        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endDate);

        Random random = new Random();
        int maxAttempts = 20;
        for (int i = 0; i < maxAttempts; i++) {
            int randomDay = random.nextInt((int) (Math.abs(endCalendar.getTimeInMillis() - startCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000)));
            Calendar contractTime = Calendar.getInstance();
            contractTime.setTime(startDate);
            if (randomDay == 0) {
                randomDay = 1;
            }
            contractTime.add(Calendar.DAY_OF_MONTH, randomDay);

            List<String> weekendList = getWeekendList(contractTime.get(Calendar.YEAR));

            SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd");
            if (!weekendList.contains(dateFormat.format(contractTime.getTime()))) {
                return contractTime.getTime();
            }
        }
        return null;
    }

    /**
     *
     * @param inputTime
     * @param k
     * @return
     */
    public static Date getReportDeadline(Date inputTime, int k) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(inputTime);
        calendar.add(Calendar.DAY_OF_MONTH, -k - 1);
        return calendar.getTime();
    }

    /**
     *
     * @param inputTime
     * @param k
     * @return
     */
    public static final OrderVo getInfo(Date inputTime, Integer k) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");

        Date reportDeadline = getReportDeadline(inputTime, k);
        Calendar reportStartCalendar = Calendar.getInstance();
        reportStartCalendar.setTime(reportDeadline);
        reportStartCalendar.add(Calendar.DAY_OF_MONTH, -(new Random().nextInt(4) + 7));
        Date startTime = reportStartCalendar.getTime();

        Calendar orderTimeCalendar = Calendar.getInstance();
        orderTimeCalendar.setTime(reportStartCalendar.getTime());

        orderTimeCalendar.add(Calendar.DAY_OF_MONTH, -(new Random().nextInt(1) + 2));
        Date orderTime = orderTimeCalendar.getTime();
        String orderNumber = dateFormat.format(orderTime) + getOrderNumber(orderTime);

        Calendar contractTimeCalendar = Calendar.getInstance();
        contractTimeCalendar.setTime(orderTime);
        Date contractTime = getAWorkDay(orderTime, reportDeadline);

        SimpleDateFormat outputDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
        return new OrderVo(outputDateFormat.format(reportDeadline),
                outputDateFormat.format(startTime), outputDateFormat.format(orderTime),
                outputDateFormat.format(contractTime), orderNumber);
    }

}
